﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WindowsFormsApplication1
{
    public class Face
    {
        public int[] texture;
        public float v0x;
        public float v0y;
        public float v1x;
        public float v1y;
        public float v2x;
        public float v2y;
        public float u0x;
        public float u0y;
        public float u1x;
        public float u1y;
        public float u2x;
        public float u2y;
        public float z;
    }

    public class Matrix
    {
        public void init(int n)
        {
            lines = n;
            m = new float[n * 4];
        }
        public int lines;//n*4
        public float[] m;
        public void multi(Matrix b, Matrix result)//result.lines is a.lines
        {
            //cpu 放矩阵单元（复用乘法器）
            //var result = [];
            for (int i = 0; i < lines * 4; i += 4)
            {
                float[] sum = new float[4];
                sum[0] = 0.0f;
                sum[1] = 0.0f;
                sum[2] = 0.0f;
                sum[3] = 0.0f;
                for (int j = 0; j < 4; j++)
                {
                    for (int k = 0; k < 4; k++)
                    {
                        sum[j] += m[i + k] * b.m[k * 4 + j];
                    }
                }
                result.m[i + 0] = sum[0];
                result.m[i + 1] = sum[1];
                result.m[i + 2] = sum[2];
                result.m[i + 3] = sum[3];
            }
            //return result;
        }

        public void init()
        {
            zero();
            //1, 0, 0, 1,
            //0, 1, 0, 1,
            //0, 0, 1, 1,
            m[0 * 4 + 0] = 1;
            m[1 * 4 + 1] = 1;
            m[2 * 4 + 2] = 1;
            //m[0 * 4 + 3] = 1;
            //m[1 * 4 + 3] = 1;
            //m[2 * 4 + 3] = 1;
            m[3 * 4 + 3] = 1;
        }
        public void zero()
        {
            for (int i = 0; i < lines; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    m[i * 4 + j] = 0;
                }
            }
        }


        static Matrix()
        {
            rotate_x = new Matrix();
            rotate_x.init(4);
            rotate_x.zero();
            //1, 0, 0, 0,
            //0, Math.cos(a), Math.sin(a), 0,
            //0, -Math.sin(a), Math.cos(a), 0,
            //0, 0, 0, 1
            rotate_x.m[0 * 4 + 0] = 1;
            rotate_x.m[3 * 4 + 3] = 1;


            rotate_y = new Matrix();
            rotate_y.init(4);
            rotate_y.zero();
            //Math.cos(a), 0, -Math.sin(a), 0,
            //0, 1, 0, 0,
            //Math.sin(a), 0, Math.cos(a), 0,
            //0, 0, 0, 1
            rotate_y.m[1 * 4 + 1] = 1;
            rotate_y.m[3 * 4 + 3] = 1;


            rotate_z = new Matrix();
            rotate_z.init(4);
            rotate_z.zero();
            //Math.cos(a), Math.sin(a), 0, 0,
            //-Math.sin(a), Math.cos(a), 0, 0,
            //0, 0, 1, 0,
            //0, 0, 0, 1
            rotate_z.m[2 * 4 + 2] = 1;
            rotate_z.m[3 * 4 + 3] = 1;


            mmove = new Matrix();
            mmove.init(4);
            mmove.zero();
            //1, 0, 0, 0,
            //0, 1, 0, 0,
            //0, 0, 1, 0,
            //tx, ty, tz, 1
            mmove.m[0 * 4 + 0] = 1;
            mmove.m[1 * 4 + 1] = 1;
            mmove.m[2 * 4 + 2] = 1;
            mmove.m[3 * 4 + 3] = 1;

            mscale = new Matrix();
            mscale.init(4);
            mscale.zero();
            //x, 0, 0, 0,
            //0, y, 0, 0,
            //0, 0, z, 0,
            //0, 0, 0, 1
            mscale.m[3 * 4 + 3] = 1;
        }


        static Matrix rotate_x;
        public void rotateX(float a)
        {
            //1, 0, 0, 0,
            //0, Math.cos(a), Math.sin(a), 0,
            //0, -Math.sin(a), Math.cos(a), 0,
            //0, 0, 0, 1
            rotate_x.m[1 * 4 + 1] = (float)Math.Cos(a);
            rotate_x.m[1 * 4 + 2] = (float)Math.Sin(a);
            rotate_x.m[2 * 4 + 1] = (float)-Math.Sin(a);
            rotate_x.m[2 * 4 + 2] = (float)Math.Cos(a);

            multi(rotate_x, this);
        }


        static Matrix rotate_y;

        public void rotateY(float a)
        {
            //Math.cos(a), 0, -Math.sin(a), 0,
            //0, 1, 0, 0,
            //Math.sin(a), 0, Math.cos(a), 0,
            //0, 0, 0, 1
            rotate_y.m[0 * 4 + 0] = (float)Math.Cos(a);
            rotate_y.m[0 * 4 + 2] = (float)-Math.Sin(a);
            rotate_y.m[2 * 4 + 0] = (float)Math.Sin(a);
            rotate_y.m[2 * 4 + 2] = (float)Math.Cos(a);

            multi(rotate_y, this);
        }

        static Matrix rotate_z;
        public void rotateZ(float a)
        {
            //Math.cos(a), Math.sin(a), 0, 0,
            //-Math.sin(a), Math.cos(a), 0, 0,
            //0, 0, 1, 0,
            //0, 0, 0, 1
            rotate_z.m[0 * 4 + 0] = (float)Math.Cos(a);
            rotate_z.m[0 * 4 + 1] = (float)Math.Sin(a);
            rotate_z.m[1 * 4 + 0] = (float)-Math.Sin(a);
            rotate_z.m[1 * 4 + 1] = (float)Math.Cos(a);

            multi(rotate_z, this);
        }


        static Matrix mmove;
        public void move(float x, float y, float z)
        {
            //1, 0, 0, 0,
            //0, 1, 0, 0,
            //0, 0, 1, 0,
            //tx, ty, tz, 1
            mmove.m[3 * 4 + 0] = x;
            mmove.m[3 * 4 + 1] = y;
            mmove.m[3 * 4 + 2] = z;

            multi(mmove, this);
        }

        static Matrix mscale;
        public void scale(float x, float y, float z)
        {
            //x, 0, 0, 0,
            //0, y, 0, 0,
            //0, 0, z, 0,
            //0, 0, 0, 1

            mscale.m[0 * 4 + 0] = x;
            mscale.m[1 * 4 + 1] = y;
            mscale.m[2 * 4 + 2] = z;

            multi(mscale, this);
        }

    }

    class Projection
    {

        public Matrix m;
        public float half_w;
        public float half_h;
        public float w;
        public float h;

        public void init(float w, float h)
        {
            this.half_w = w / 2;
            this.half_h = h / 2;
            this.w = w;
            this.h = h;
            float h_fov = (float)Math.PI / 3;
            float v_fov = (float)Math.PI / 3 * (h / w);
            float NEAR = 0.1f;//near_plane
            float FAR = 100;//far_plane
            float RIGHT = (float)Math.Tan(h_fov / 2);
            float LEFT = -RIGHT;
            float TOP = (float)Math.Tan(v_fov / 2);
            float BOTTOM = -TOP;

            float m00 = 2 / (RIGHT - LEFT);
            float m11 = 2 / (TOP - BOTTOM);
            float m22 = (FAR + NEAR) / (FAR - NEAR);
            float m32 = -2 * NEAR * FAR / (FAR - NEAR);
            m = new Matrix();
            m.init(4);
            m.zero();
            //m00, 0, 0, 0,
            //0, m11, 0, 0,
            //0, 0, m22, 1,
            //0, 0, 0, 0
            m.m[0 * 4 + 0] = m00;
            m.m[1 * 4 + 1] = m11;
            m.m[2 * 4 + 2] = m22;
            m.m[2 * 4 + 3] = 1;
        }
    }
    class Camera
    {
        public float x = -5;
        public float y = 6;
        public float z = -25;
        public float direction = 0;
        public float angle = 0;
        public float moving_speed = 1;
        public float rotation_speed = 0.015f;

        Matrix m;
        Matrix a;
        Matrix ret;
        public Camera()
        {
            m = new Matrix();
            m.init(4);
            a = new Matrix();
            a.init(4);
            ret = new Matrix();
            ret.init(4);
        }
        public Matrix cameraMatrix()
        {
            a.zero();
            //var a = [
            //1, 0, 0, 0,
            //0, 1, 0, 1,
            //0, 0, 1, 0,
            //-this.x, -this.y, -this.z, 1
            //];
            a.m[0 * 4 + 0] = 1;
            a.m[1 * 4 + 1] = 1;
            a.m[1 * 4 + 3] = 1;
            a.m[2 * 4 + 2] = 1;
            a.m[3 * 4 + 0] = -this.x;
            a.m[3 * 4 + 1] = -this.y;
            a.m[3 * 4 + 2] = -this.z;
            a.m[3 * 4 + 3] = 1;

            m.zero();
            //1, 0, 0, 1,
            //0, 1, 0, 1,
            //0, 0, 1, 1,
            m.m[0 * 4 + 0] = 1;
            m.m[1 * 4 + 1] = 1;
            m.m[2 * 4 + 2] = 1;
            m.m[0 * 4 + 3] = 1;
            m.m[1 * 4 + 3] = 1;
            m.m[2 * 4 + 3] = 1;

            //var rotate = Lib3d.rotateX(this.angle);
            //m.multi(rotate, m);
            m.rotateX(this.angle);

            //rotate = Lib3d.rotateY(this.direction);
            //m.multi(rotate, m);
            m.rotateY(this.direction);

            ret.zero();
            //0, 0, 0, 0,
            //0, 0, 0, 0,
            //0, 0, 0, 0,
            //0, 0, 0, 0
            for (var i = 0; i < 3; i++)
            {
                for (var j = 0; j < 3; j++)
                {
                    ret.m[i * 4 + j] = m.m[j * 4 + i];
                }
                ret.m[i * 4 + 3] = 0;
            }
            ret.m[3 * 4 + 0] = 0;
            ret.m[3 * 4 + 1] = 0;
            ret.m[3 * 4 + 2] = 0;
            ret.m[3 * 4 + 3] = 1;
            a.multi(ret, a);
            return a;
        }
    };


}
